在 LLMOps 的世界裡,向量資料庫 (Vector Database) 幾乎是 RAG(Retrieval-Augmented Generation) 架構的核心。
因為大語言模型(LLM)沒有「記憶」企業內部知識,我們必須把文件切片 (chunking)、轉成向量,再存進一個可以高效檢索的資料庫。 換句話說,在 RAG (Retrieval-Augmented Generation) 流程裡,向量資料庫的角色是「大腦記憶庫」:
RAG 流程
Day 4 我們先從 向量資料庫 開始,學會怎麼存放與檢索向量;接著在 Day5 再來比較不同的 Embedding 模型,看看不同轉換方式會如何影響檢索品質。
傳統的關鍵字搜尋 (keyword search) 很難理解語意:
沒有向量資料庫,RAG 就沒有辦法 以真實文件片段支撐回答,只能憑空生成,於是乎「幻覺」就出現了。除了語意檢索之外,向量資料庫會用 ANN (Approximate Nearest Neighbor) 索引[註1](如 HNSW、IVF、PQ)來在毫秒內完成搜尋,而不是單純暴力比對。
同時站在維運角度,部分專門型向量資料庫支援動態更新、可擴展與可觀測,可以讓 RAG 系統擁有真正可靠且持續進化的「長期記憶」。
[註1] Understanding the approximate nearest neighbor (ANN) algorithm
HNSW(Hierarchical Navigable Small World):用圖結構加速搜尋;
IVF(Inverted File):先把向量分桶,再在桶內搜尋;
PQ(Product Quantization):透過壓縮方式降低維度,加快比對速度。
這些索引方法犧牲了一點精確度,但可以換取大幅提升的搜尋速度
工具 | 型態 | 優點 | 缺點 | 收費 | 適合場景 |
---|---|---|---|---|---|
FAISS | 本地庫 (Facebook AI 開源) | 輕量、快、離線可用 | 不支援分散式,僅限單機記憶體運算 | ✅ 完全免費 | 個人、小專案、研究 |
Weaviate | 開源 + 雲服務 | Graph-like schema、多功能 | 需額外啟動容器,維運比 FAISS 複雜 | 本地免費;WCS 雲端有免費額度,超額需付費 | 想要開源 & 社群支持 |
Pinecone | SaaS 雲端服務 | 無痛上手、可擴展 | 僅支援自家雲服務,無法自架 | 有免費方案(數萬向量),進入商用需付費 | 快速 MVP、商用 |
Milvus | 開源 (Zilliz) + 雲服務 | 分散式、大規模數據 | 需搭配 etcd / pulsar,架構偏重 | 本地免費;Zilliz Cloud 有免費額度,超額需付費 | 海量資料 (> 億級) |
經過 Day3 的環境準備,我們已經有 Docker + Conda 環境了。
今天的測試只需要額外安裝幾個客戶端:
pip install weaviate-client pinecone faiss-cpu pymilvus
GitHub Repo 裡面有打包好的 Conda 設定,也可以直接用 Conda 啟動沒問題👌
接下來我們會展示其中三個向量資料庫的操作,做這些 Demo 的目的是要 理解不同向量資料庫在真實專案中扮演的角色。
FAISS 是最輕量的選項,它只會把向量暫時存在記憶體,並不具備真正的「資料庫」功能,這樣的特性讓它非常適合用來 驗證流程 / 做快速實驗:
import faiss
import numpy as np
# 模擬兩個向量 (128 維)
d = 128
xb = np.random.random((5, d)).astype('float32')
xq = np.random.random((1, d)).astype('float32')
index = faiss.IndexFlatL2(d) # L2 距離
index.add(xb) # 使用查詢向量 xq 建立索引
D, I = index.search(xq, k=2) # 查詢
print("相似度距離:", D)
print("相似向量索引:", I)
執行結果:
❯ python faiss_demo.py
相似度距離: [[17.419899 17.930994]]
相似向量索引: [[0 3]]
在這個範例中,使用者的問題先被轉換成向量 xq
,再透過 FAISS 在資料庫中檢索,接著在用 FAISS 建立的向量索引中進行相似度搜尋,結果顯示它與第 0 與第 3 筆文件向量最接近。實務上,會把這兩筆對應的文件片段取出,交給 LLM 生成答案。
雖然能序列化索引檔案(write_index
/ read_index
),但依舊不適合做大型專案的持久化。
在這個範例裡,我們不是單純在記憶體裡建一個索引,而是啟動了一個 真正的向量資料庫服務(Weaviate 容器),並且支援 schema、持久化、API 存取。
Weaviate 提供 Docker 版,可以快速啟動:
# docker-compose.yml
version: '3.4'
services:
weaviate:
image: semitechnologies/weaviate:1.24.2
ports:
- "8080:8080"
environment:
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
啟動
docker-compose up -d
測試連線:
import weaviate
HOST, REST_PORT, GRPC_PORT = "localhost", 8080, 50052
client = weaviate.connect_to_local(host=HOST, port=REST_PORT, grpc_port=GRPC_PORT)
嘗試插入文字:
# insert_docs.py
import numpy as np
import weaviate
from weaviate.classes.config import Property, DataType, Configure
HOST, REST_PORT, GRPC_PORT = "localhost", 8080, 50052
client = weaviate.connect_to_local(host=HOST, port=REST_PORT, grpc_port=GRPC_PORT)
try:
print("ready?", client.is_ready())
# 如果已經有 Docs 集合,刪掉重建
if "Docs" in client.collections.list_all():
client.collections.delete("Docs")
# 建立一個 Docs collection
client.collections.create(
name="Docs",
properties=[Property(name="text", data_type=DataType.TEXT)],
vector_config=Configure.Vectors.self_provided(name="default"),
)
col = client.collections.get("Docs")
# 插入兩筆資料
dim = 128
docs = [
"RAG 是 Retrieval-Augmented Generation",
"請假流程需要先主管簽核,再送人資系統",
]
for text in docs:
vec = np.random.random(dim).astype("float32").tolist()
obj_uuid = col.data.insert({"text": text}, vector={"default": vec})
print(f"✅ inserted: {text}, uuid={obj_uuid}")
finally:
client.close()
執行結果:
❯ python insert_docs.py
ready? True
✅ inserted: RAG 是 Retrieval-Augmented Generation, uuid=17004906-52ba-495c-89c0-24707063bc7a
✅ inserted: 請假流程需要先主管簽核,再送人資系統, uuid=cff05618-bbca-469d-b6ef-cf2bf2217d4d
再寫另外一隻程式,嘗試隨機生成亂數,並查詢向量距離相近的文字:
❯ python query_docs.py
📦 total_count: 2
🔍 query result: [{'text': '請假流程需要先主管簽核,再送人資系統'}]
❯ python query_docs.py
📦 total_count: 2
🔍 query result: [{'text': 'RAG 是 Retrieval-Augmented Generation'}]
這證明了和 FAISS 單純記憶體索引相比,Weaviate 能把資料存起來,並透過 API 對外提供服務。
在 Pinecone 的範例中,我們建立了一個雲端索引,並測試與其連線。雖然這個 Demo 還沒插入具體的文件內容,但它展示了 如何在雲端建立一個可擴展的向量檢索服務。
需要到 Pinecone 官網註冊帳號,然後設定環境變數:
export PINECONE_API_KEY="你的API key" #或者可以編輯.env 檔案
測試連線:
# pinecone_demo.py
from pinecone import Pinecone, ServerlessSpec
import os
import numpy as np
from dotenv import load_dotenv
# 載入 .env
load_dotenv()
# 讀取金鑰
PINECONE_API_KEY = os.getenv("PINECONE_API_KEY")
if not PINECONE_API_KEY:
raise RuntimeError("❌ 找不到 PINECONE_API_KEY,請確認 .env 或環境變數設定")
# 初始化
pc = Pinecone(api_key=PINECONE_API_KEY)
index_name = "demo-index"
# 建立 index(如果不存在)
if index_name not in [idx["name"] for idx in pc.list_indexes()]:
pc.create_index(
name=index_name,
dimension=128,
metric="cosine",
spec=ServerlessSpec(cloud="aws", region="us-east-1"),
)
index = pc.Index(index_name)
# 定義文件,可以在這邊修改測試資料
docs = [
{"id": "101", "text": "員工請假需要提前三天申請"},
{"id": "102", "text": "差旅報銷需附上發票與行程單"},
{"id": "103", "text": "出差前需要完成出差申請表"},
]
# 先檢查目前 index 狀態
stats = index.describe_index_stats()
existing_count = stats["total_vector_count"]
if existing_count == 0:
print("🆕 第一次執行:插入文件")
else:
print("♻️ 文件已存在:這次會更新向量內容")
# 插入 / 更新文件
vectors = []
for doc in docs:
vec = np.random.random(128).astype("float32").tolist()
vectors.append((doc["id"], vec, {"text": doc["text"]}))
index.upsert(vectors=vectors)
# 再檢查狀態
new_stats = index.describe_index_stats()
print(f"✅ 向量總數: {new_stats['total_vector_count']}")
# 模擬查詢
query_vec = np.random.random(128).astype("float32").tolist()
res = index.query(vector=query_vec, top_k=2, include_metadata=True)
print("\n🔍 查詢結果:")
for match in res["matches"]:
print(f"- {match['metadata']['text']} (score={match['score']:.4f})")
執行結果:
❯ python pinecone_demo.py
♻️ 文件已存在:這次會更新向量內容
✅ 向量總數: 10
🔍 查詢結果:
- 差旅報銷需附上發票與行程單 (score=0.7656)
- Cache 可以降低延遲和成本 (score=0.7636)
可以在雲端 console
上面觀察 metrics
:
不同於 FAISS 僅存在記憶體、Weaviate 要自己架設服務,Pinecone 提供 託管式雲端服務:
id
重複插入,Pinecone 會自動執行 upsert(更新或新增),避免重複資料。完整可執行專案 已經放在 GitHub,完整的細節請看
README.md
。
Milvus 適合大規模應用,時間關係我不會 Demo,或是之後我再找時間補上, Milvus 可以自建,也可以使用原廠提供的雲端託管服務 (Zilliz Cloud)。
今天我們
明天(Day 5)我們將比較不同 Embedding 模型(OpenAI、HuggingFace、BGE、Cohere) 並且建立一個小型 Embedding 測試報告, 為之後的 RAG pipeline 打下基礎 🚀。